import KDBush from "kdbush";
import { App, GeoUtils,  Point } from "../Engine";
import Events from "../application/constants/Events";
import Const from "../application/constants/Const";
import GPSUtils from "../utils/GPSUtils";
import GeometryUtil from "../renderExtension/utils/GeometryUtil";
import { Group } from "three/src/objects/Group";

/** 聚合图层*/
class Cluster {
    private app: App;
    minclusterSize: any;
    minclusterHeight: any;
    maxclusterHeight: any;
    pixelRange = 200;
    minimumClusterSize = 3;
    clusterEntities: any = {};
    clusterHTML: string;
    layer: Group;
    generateClusterMarker: Function;

    public elements = [];

    /**
     * 构造函数
     * @param options 配置项
     * @param options.minclusterSize 最小聚合对象数量
     * @param options.clusterHTML 聚合后的html标记显示的html内容
     * @param options.pixelRange 聚合半径，值越大，聚合程度越高
     * @param options.generateClusterMarker 生成聚合标记的回调函数，可以返回一个marker
     * @example   let cluster = new GE.Cluster({
        pixelRange: 30, //这个值越大，点位聚合程度越高
        //generateClusterMarker表示生成聚合标记的回调函数，如果不传这个参数被聚合的对象会直接被隐藏
        generateClusterMarker: function (clusterData) {
            const div = document.createElement('div');
            const button = document.createElement('button');
            button.innerText = clusterData.count;  //clusterData的count属性代表聚合对象的数量
            button.onclick = function (e) {
                //clusterData的datas属性代表被聚合的对象
                console.log(clusterData.datas);
            }
            div.appendChild(button);
            let marker = new GE.HtmlMarker(div);//这里用的是htmlmarker表示聚合标记，也可以自行替换成模型或者其他对象
            return marker;
        }
     );
     /将需要被聚合的对象添加到cluster中，被聚合的对象可以是POI，可以是htmlMarker，也可以是模型，多边形，动效组件等对象
     cluster.add(pois); 
     * @returns 
     */
    constructor(options: any) {
        this.app = App.getInstance();
        this.layer = new Group();
        this.app.add(this.layer);
        if (!this.layer) {
            return;
        }

        this.minclusterSize = options['minclusterSize'] || 1;
        this.minclusterHeight = options['minclusterHeight'] || 0;
        this.maxclusterHeight = options['maxclusterHeight'] || 1000000;
        this.clusterHTML = options['clusterHTML'] || '<input type="button" class="button"  value={clusterNumber}>';
        this.pixelRange = options['pixelRange'] || 200;
        if (this.minclusterHeight > this.maxclusterHeight) {
            this.maxclusterHeight += this.minclusterHeight;
        }
        this.generateClusterMarker = options['generateClusterMarker'] || function () {
            return null;
        };

        this.initEvents();
    }

    /**
     * 添加一个需要聚合的对象
     * @param element 对象
     */
    public add(element) {
        if (Array.isArray(element)) {
            this.elements = this.elements.concat(element);
        }
        else {
            this.elements.push(element);
        }

        this.update();
    }

    /**
     * 移除一个需要聚合的对象
     * @param element 对象
     */
    public remove(element) {
        var index = this.elements.indexOf(element);
        if (index > -1) {
            this.elements.splice(index, 1);
        }
    }

    protected initEvents() {
        // this.app.on(Events.Resize, this.update.bind(this), this);
        // this.app.on(Events.CameraChangeEnd, this.update.bind(this), this);
        this.app.on(Events.ZoomChange, this.update.bind(this), this);
    }

    public dispose() {
        this.clusterEntities = null;
        this.elements = [];
        this.layer.clear();
        this.app.remove(this.layer);
        // this.app.off(Events.Resize, this.update, this);
        // this.app.off(Events.CameraChangeEnd, this.update, this);
        this.app.off(Events.ZoomChange, this.update, this);
    }

    public update() {
        this.layer.clear();
        let scope = this;
        const kdbush = new KDBush(this.elements, function (element) {
            return GPSUtils.PointToContainer(element.position.clone()).getX();
        }, function (element) {
            return GPSUtils.PointToContainer(element.position.clone()).getY();
        }, 64, Float64Array);

        let screenW = this.app.getSize().width;
        let screenH = this.app.getSize().height;
        const col = Math.ceil(screenW / this.pixelRange);
        const row = Math.ceil(screenH / this.pixelRange);

        let tempIds = [];
        let clusterIds = [];
        for (let i = 0; i < col; i++) {
            for (let j = 0; j < row; j++) {
                const ids = kdbush.range(i * this.pixelRange, j * this.pixelRange, (i + 1) * this.pixelRange, (j + 1) * this.pixelRange)
                if (ids.length >= this.minimumClusterSize) {
                    // 计算聚合对象们得质心经纬度
                    const rangedPoints = [];
                    let id = "";
                    let entities = [];
                    for (let k = 0; k < ids.length; k++) {
                        kdbush.points[ids[k]].visible = false;
                        tempIds.push(kdbush.points[ids[k]].id);
                        rangedPoints.push({
                            x: kdbush.points[ids[k]].position.x,
                            y: kdbush.points[ids[k]].position.y
                        });
                        id += kdbush.points[ids[k]].id + ",";
                        entities.push(kdbush.points[ids[k]]);
                    }

                    clusterIds.push({
                        id: id,
                        entities: entities,
                        rangedPoints: rangedPoints
                    });

                }
            }
        }

        //添加不在当前cluster几何中的聚集对象
        for (let i = 0; i < clusterIds.length; i++) {
            if (!this.clusterEntities[clusterIds[i].id]) {
                GeometryUtil.sortClockwise(clusterIds[i].rangedPoints);
                // 聚合后的点位按重心计算而不是质心
                var gravityCenter = GeometryUtil.getPolygonAreaCenter(clusterIds[i].rangedPoints)

                //创建聚合对象元素,暂时为HTMLObject
                const clusterPosition = new Point(gravityCenter.x, gravityCenter.y, 0.1, Const.EPSGType.EPSGWeb);
                const div = document.createElement('div');
                div.innerHTML = this.clusterHTML.replace("{clusterNumber}", clusterIds[i].entities.length);
                let marker = this.generateClusterMarker({
                    count: clusterIds[i].entities.length,
                    datas: clusterIds[i].entities
                });
                if (marker) {
                    marker.position.set(clusterPosition.x, clusterPosition.y, clusterPosition.z);
                    marker['entities'] = clusterIds[i].entities;
                    this.layer.add(marker);

                    this.clusterEntities[clusterIds[i].id] = marker;
                }
            }
        }

        //显示不需要隐藏的对象
        let keys = Object.keys(this.clusterEntities);
        for (let i = 0; i < keys.length; i++) {
            let remain = false;
            for (let j = 0; j < clusterIds.length; j++) {
                if (clusterIds[j].id == keys[i]) {
                    remain = true;
                    break;
                }
            }

            if (!remain) {
                for (let j = 0; j < this.clusterEntities[keys[i]].entities.length; j++) {
                    if (tempIds.indexOf(this.clusterEntities[keys[i]].entities[j].id) == -1)
                        this.clusterEntities[keys[i]].entities[j].visible = true;
                }

                this.remove(this.clusterEntities[keys[i]]);
                //this.clusterEntities[keys[i]].dispose();
                delete this.clusterEntities[keys[i]];
            }
        }
    }
}
export { Cluster }